////////////////////////////////////////////////////////////////////////////
//
//  CryEngine Source File.
//  Copyright (C), Crytek, 1999-2010.
// -------------------------------------------------------------------------
//  File name: RefPicture.cpp
//  Version:   v1.00
//  Created:   20-04-2010 by Dongjoon Kim
//  Description:
// -------------------------------------------------------------------------  
//  History:
//
//////////////////////////////////////////////////////////////////////////// 

#include "StdAfx.h"
#include "RefPicture.h"
#include "Geometry/EdMesh.h"
#include "../Material/MaterialManager.h"
#include "Viewport.h"

#define REFERENCE_PICTURE_MATERAIL_TEMPLATE "Editor/Materials/refpicture.mtl"
#define REFERENCE_PICTURE_BASE_OBJECT "Editor/Objects/refpicture.cgf"

#define DISPLAY_2D_SELECT_LINEWIDTH 2
#define DISPLAY_2D_SELECT_COLOR RGB(225,0,0)
#define DISPLAY_GEOM_SELECT_COLOR ColorB(250,0,250,30)
#define DISPLAY_GEOM_SELECT_LINE_COLOR ColorB(255,255,0,160)

_smart_ptr<CMaterial> g_RefPictureMaterialTemplate = 0;

//-----------------------------------------------------------------------------
IMPLEMENT_DYNCREATE(CRefPicture,CBaseObject)

//-----------------------------------------------------------------------------
IMaterial* GetRefPictureMaterial()
{
	if(!g_RefPictureMaterialTemplate)
	{
		CString filename = Path::MakeGamePath( REFERENCE_PICTURE_MATERAIL_TEMPLATE );

		XmlNodeRef mtlNode = GetISystem()->LoadXmlFromFile( filename );
		if (mtlNode)
		{
			// Create a template material without registering DB.
			g_RefPictureMaterialTemplate = GetIEditor()->GetMaterialManager()->CreateMaterial("RefPicture", mtlNode,MTL_FLAG_UIMATERIAL);
		}
	}

	IMaterial* pMat = GetIEditor()->Get3DEngine()->GetMaterialManager()->CloneMaterial(g_RefPictureMaterialTemplate->GetMatInfo());
	return pMat;
}

//-----------------------------------------------------------------------------
CRefPicture::CRefPicture()
:m_pMaterial(0)
,m_pRenderNode(0)
,m_pGeometry(0)
,m_aspectRatio(1.f)
{
	mv_scale.Set(1.0f);
	mv_scale.SetLimits(0.001f, 100.f);

	mv_opacity.Set(100.0f);
	mv_opacity.SetLimits(1.f, 100.f);

	AddVariable( mv_picFilepath,"File",functor(*this,&CRefPicture::OnVariableChanged),IVariable::DT_FILE);
	AddVariable( mv_scale,"Scale",functor(*this,&CRefPicture::OnVariableChanged));
	AddVariable( mv_opacity,"Opacity",functor(*this,&CRefPicture::OnVariableChanged));

	m_pMaterial = GetRefPictureMaterial();
}

//-----------------------------------------------------------------------------
void CRefPicture::Display( DisplayContext &dc )
{
	if (dc.flags & DISPLAY_2D)
	{
		if (IsSelected())
		{
			dc.SetLineWidth(DISPLAY_2D_SELECT_LINEWIDTH);
			dc.SetColor(DISPLAY_2D_SELECT_COLOR);
		}
		else
		{
			dc.SetColor( GetColor() );
		}

		dc.PushMatrix( GetWorldTM() );
		dc.DrawWireBox( m_bbox.min,m_bbox.max );

		dc.PopMatrix();
		
		if (IsSelected())
			dc.SetLineWidth(0);

		if( m_pGeometry )
			m_pGeometry->Display( dc );
		return;
	}

	if (m_pGeometry)
	{
		if (!IsHighlighted())
		{
			m_pGeometry->Display( dc );
		}
		else if (gSettings.viewports.bHighlightMouseOverGeometry)
		{
			SGeometryDebugDrawInfo debugDrawInfo;
			debugDrawInfo.tm = GetWorldTM();
			debugDrawInfo.color = DISPLAY_GEOM_SELECT_COLOR;
			debugDrawInfo.lineColor = DISPLAY_GEOM_SELECT_LINE_COLOR;
			debugDrawInfo.bExtrude = true;
			m_pGeometry->DebugDraw( debugDrawInfo );
		}
	}

	DrawDefault( dc );
}

//-----------------------------------------------------------------------------
bool CRefPicture::HitTest( HitContext &hc )
{
	Vec3 localHit;
	Vec3 localRaySrc = hc.raySrc;
	Vec3 localRayDir = hc.rayDir;

	localRaySrc = m_invertTM.TransformPoint( localRaySrc );
	localRayDir = m_invertTM.TransformVector(localRayDir).GetNormalized();

	if (Intersect::Ray_AABB( localRaySrc,localRayDir,m_bbox, localHit ))
	{
		if (hc.b2DViewport)
		{
			// World space distance.
			hc.dist = hc.raySrc.GetDistance(GetWorldTM().TransformPoint(localHit));
			return true;
		}

		
		if(m_pRenderNode)
		{
			IStatObj *pStatObj = m_pRenderNode->GetEntityStatObj(0);
			if (pStatObj)
			{
				SRayHitInfo hi;
				hi.inReferencePoint = localRaySrc;
				hi.inRay = Ray(localRaySrc,localRayDir);
				if(pStatObj->RayIntersection(hi))
				{
					hc.dist = hc.raySrc.GetDistance(GetWorldTM().TransformPoint(hi.vHitPos));
					return true;
				}
				return false;
			}
		}
	}
	return false;
}

//-----------------------------------------------------------------------------
void CRefPicture::GetLocalBounds( AABB &box )
{
	box = m_bbox;
}

int CRefPicture::MouseCreateCallback( CViewport *view,EMouseEvent event,CPoint &point,int flags )
{
	if (event == eMouseMove || event == eMouseLDown)
	{
		Vec3 pos;
		if (GetIEditor()->GetAxisConstrains() != AXIS_TERRAIN)
		{
			pos = view->MapViewToCP(point);
		}
		else
		{
			// Snap to terrain.
			bool hitTerrain;
			pos = view->ViewToWorld( point,&hitTerrain );
			if (hitTerrain)
			{
				pos.z = GetIEditor()->GetTerrainElevation(pos.x,pos.y);
			}
		}

		pos = view->SnapToGrid(pos);
		SetPos( pos );
		if (event == eMouseLDown)
			return MOUSECREATE_OK;
		return MOUSECREATE_CONTINUE;
	}
	return CBaseObject::MouseCreateCallback( view,event,point,flags );
}


//-----------------------------------------------------------------------------
XmlNodeRef CRefPicture::Export( const CString &levelPath,XmlNodeRef &xmlNode )
{
	// Don't export this. Only relevant for editor.
	return 0;
}

//-----------------------------------------------------------------------------
void CRefPicture::PostClone( CBaseObject *pFromObject,CObjectCloneContext &ctx )
{
	SetScale( pFromObject->GetScale() );
}

//-----------------------------------------------------------------------------
bool CRefPicture::CreateGameObject()
{
	// Load geometry
	if(!m_pGeometry)
	{
		m_pGeometry = CEdMesh::LoadMesh(REFERENCE_PICTURE_BASE_OBJECT);
		assert(m_pGeometry);

		if(!m_pGeometry)
			return false;
	}

	// Create render node
	if(!m_pRenderNode)
	{
		m_pRenderNode = GetIEditor()->Get3DEngine()->CreateRenderNode( eERType_Brush );

		if (m_pGeometry)
		{
			m_pGeometry->GetBounds( m_bbox );
			Matrix34A tm = GetWorldTM();
			m_pRenderNode->SetEntityStatObj( 0,m_pGeometry->GetGeometry(),&tm );
			m_pGeometry->GetGeometry()->SetMaterial(m_pMaterial);
		}
	}

	// Apply material & reference picture image

	UpdateImage(mv_picFilepath);

	return true;
}

//-----------------------------------------------------------------------------
void CRefPicture::Done()
{
	if (m_pRenderNode)
	{
		GetIEditor()->Get3DEngine()->DeleteRenderNode(m_pRenderNode);
		m_pRenderNode = 0;
	}
	
	if (m_pGeometry)
		m_pGeometry = 0;

	if(m_pMaterial)
		m_pMaterial = 0;

	CBaseObject::Done();
}

//-----------------------------------------------------------------------------
void CRefPicture::InvalidateTM( int nWhyFlags )
{
	CBaseObject::InvalidateTM(nWhyFlags);

	if (!(nWhyFlags & TM_RESTORE_UNDO)) // Can skip updating game object when restoring undo.
	{
		if (m_pRenderNode)
		{
			Matrix34A tm = GetWorldTM();
			m_pRenderNode->SetEntityStatObj( 0,m_pGeometry->GetGeometry(),&tm );
		}
	}

	m_invertTM = GetWorldTM();
	m_invertTM.Invert();
}

//-----------------------------------------------------------------------------
void CRefPicture::OnVariableChanged(IVariable*	piVariable)
{
	if( !strcmp(piVariable->GetName(),"File") || !strcmp(piVariable->GetName(),"Opacity") )
		UpdateImage(mv_picFilepath);
	else if(!strcmp(piVariable->GetName(), "Scale") )
		ApplyScale((float)mv_scale);
}

//-----------------------------------------------------------------------------
void CRefPicture::UpdateImage(const CString& picturePath)
{
	if(!m_pMaterial || !m_pRenderNode)
		return;

	SShaderItem& si( m_pMaterial->GetShaderItem() );
	SInputShaderResources isr( si.m_pShaderResources );
	isr.m_Textures[ EFTT_DIFFUSE ].m_Name = picturePath;
	isr.m_Opacity = (float)mv_opacity / 100.f;
	
	SShaderItem siDst( GetIEditor()->GetRenderer()->EF_LoadShaderItem( si.m_pShader->GetName(), true, 0, &isr, si.m_pShader->GetGenerationMask() ) );
	m_pMaterial->AssignShaderItem( siDst );

	m_pRenderNode->SetMaterial(m_pMaterial);

	CImage image;
	if(CImageUtil::LoadImage( picturePath,image ))
	{
		float w = (float)image.GetWidth();
		float h = (float)image.GetHeight();

		m_aspectRatio = (h!=0.f ? w/h : 1.f);
	}
	else
	{
		m_aspectRatio = 1.f;
	}

	ApplyScale((float)mv_scale);
}

//-----------------------------------------------------------------------------
void CRefPicture::ApplyScale(float fScale)
{
	float scaleHeight = fScale;
	float scaleWidth = m_aspectRatio*fScale;
	float scaleTickness = 1.f;

	CBaseObject::SetScale(Vec3(scaleWidth, scaleTickness, scaleHeight));
}